#include <stdlib.h>

#include "collisions.h"
#include "projectiles.h"
#include "def_etat_bataille.h"
#include "base_donnees.h"
#include "carte_zone.h"
#include "manipulation_cases.h"
#include "manipulation_ennemis.h"

#include "constantes_et_macros.h"



/**
 * \fn static void infliger_degats(unsigned int* pv, const struct projectile* proj)
 * \brief Decremente une valeur entiere positive en fonction de la puissance d'un projectile.
 *        Si les degats sont trop lourds, les pv ne passent pas en negatifs, mais tombent a zero.
 *
 * \param[in,out] pv L'adresse du total de pv a modifier
 * \param[in] proj L'adresse du projectile qui inflige les degats
 */
static void infliger_degats(unsigned int* pv, const struct projectile* proj)
{
    unsigned int degats;

    degats = proj->puissance;
    if(*pv <= degats)
        *pv = 0;
    else
        *pv = *pv - degats;
}



char voir_si_intersection(const unsigned int x_min_a, const unsigned int y_min_a,
                          const unsigned int taille_x_a, const unsigned int taille_y_a,
                          const unsigned int x_min_b, const unsigned int y_min_b,
                          const unsigned int taille_x_b, const unsigned int taille_y_b)
{
    return (x_min_a + taille_x_a > x_min_b &&
            y_min_a + taille_y_a > y_min_b &&
            x_min_b + taille_x_b > x_min_a &&
            y_min_b + taille_y_b > y_min_a);
}



void gerer_apparition_projectile(struct carte_zone* carte,
                                 struct etat_bataille* bataille,
                                 const char type_attaque,
                                 const struct base_donnees* donnees)
{
    unsigned int x_orig, y_orig;
    unsigned char taille_projectile;
    struct projectile* p_proj;
    char rectangle_suivant_x, rectangle_suivant_y;
    char decalage_x, decalage_y;
    int num_ligne, decalage;
    unsigned int x_case, y_case, x_debut_ligne, y_debut_ligne;
    char collision;
    struct case_destructible* p_case_destructible;

    p_proj = bataille->projectiles.prem;
    taille_projectile = get_taille_projectile(donnees, p_proj);
    /*
     * calcul des coordonnees de la premiere case de la premiere ligne
     * et des mouvements a faire pour parcourir correctement la zone
     */
    switch(p_proj->orientation)
    {
        case HAUT:
            x_orig = p_proj->coord_x;
            y_orig = p_proj->coord_y + taille_projectile - 1;
            rectangle_suivant_x = 0,        rectangle_suivant_y = -1;
            decalage_x = 1,                 decalage_y = 0;
            break;
        case DROITE:
            x_orig = p_proj->coord_x;
            y_orig = p_proj->coord_y;
            rectangle_suivant_x = 1,        rectangle_suivant_y = 0;
            decalage_x = 0,                 decalage_y = 1;
            break;
        case BAS:
            x_orig = p_proj->coord_x;
            y_orig = p_proj->coord_y;
            rectangle_suivant_x = 0,        rectangle_suivant_y = 1;
            decalage_x = 1,                 decalage_y = 0;
            break;
        case GAUCHE:
            x_orig = p_proj->coord_x + taille_projectile - 1;
            y_orig = p_proj->coord_y;
            rectangle_suivant_x = -1,       rectangle_suivant_y = 0;
            decalage_x = 0,                 decalage_y = 1;
            break;
        default:
            break;
    }
    collision = FAUX;
    x_debut_ligne = x_orig;
    y_debut_ligne = y_orig;
    for(num_ligne = 0;  num_ligne < taille_projectile;  num_ligne++)
    {
        x_case = x_debut_ligne;
        y_case = y_debut_ligne;
        for(decalage = 0;  decalage < taille_projectile;  decalage++)
        {
            if(get_case_constante_grille(carte, x_case, y_case)->relief == RELIEF_POSITIF)
                collision = VRAI;
            if((p_case_destructible = get_case_destructible(carte, x_case, y_case)))
            {
                collision = VRAI;
                endommager_case_destructible(carte, x_case, y_case);
            }
            x_case += decalage_x;
            y_case += decalage_y;
        }
        /*
         * si on trouve une collision, nul besoin d'aller voir plus loin
         * Si on n'a pas encore rencontre d'obstacle, il faut voir si un
         * adversaire provoque une collision.
         */
        if(!collision)
        {
            collision = rechercher_adversaires_dans_zone(bataille, p_proj,
                                             x_debut_ligne, y_debut_ligne,
                                             (taille_projectile - 1) * decalage_x + 1,
                                             (taille_projectile - 1) * decalage_y + 1,
                                             donnees);
        }
        /* fin de la recherche des collisions avec les individus */
        if(type_attaque == CONTACT || collision)
        {
            /*
             * dans le cas d'une attaque de corps a corps, on utilise tout de
             * meme un projectile, mais celui-ci est detruit ici-meme, apres
             * son apparition, et ne sera meme pas visible a l'ecran
             */
            supprimer_projectile(&bataille->projectiles, p_proj);
            return;
        }
        x_debut_ligne += rectangle_suivant_x;
        y_debut_ligne += rectangle_suivant_y;
    }
}



char voir_si_deplacement_possible(const char direction, unsigned int x,
                                  unsigned int y,
                                  const struct carte_zone* carte, const unsigned char taille,
                                  const struct etat_bataille* bataille, const char volant,
                                  const struct base_donnees* donnees)
{
    int i, j;
    unsigned char t_enn;
    char relief_case;
    /*
     * le "unsigned" est garanti par le mur d'enceinte, qui ne permet pas
     * de tenter un deplacement en coordonnees negatives
     */
    unsigned int x_min, y_min;
    unsigned char taille_x, taille_y;
    struct ennemi* p_enn;

    /*
     * premierement, il faut definir les coordonnees du rectangle qui va etre
     * occupe par l'element et qui ne l'etait pas auparavant
     */
    switch(direction)
    {
        case HAUT:
            x_min = x,           y_min = y - 1;
            taille_x = taille,   taille_y = 1;
            break;
        case DROITE:
            x_min = x + taille,  y_min = y;
            taille_x = 1,        taille_y = taille;
            break;
        case BAS:
            x_min = x,           y_min = y + taille;
            taille_x = taille,   taille_y = 1;
            break;
        case GAUCHE:
            x_min = x - 1,       y_min = y;
            taille_x = 1,        taille_y = taille;
            break;
        default:
            /* le cas du NEUTRE tombera ici */
            return FAUX;
            break;
    }
    /* ensuite, on regarde si le rectangle defini est libre */
    for(j = y_min;  j < y_min + taille_y;  j++)
        for(i = x_min;  i < x_min + taille_x;  i++)
        {
            relief_case = get_case_constante_grille(carte, i, j)->relief;
            if(relief_case == RELIEF_POSITIF || (relief_case == RELIEF_NEGATIF && !volant))
                return FAUX;
            if(get_case_destructible(carte, i, j) != NULL)
                return FAUX;
        }
    /* on regarde si un ennemi bloque le passage */
    p_enn = bataille->ennemis.prem;
    while(p_enn)
    {
        t_enn = get_taille(donnees, p_enn);
        if(voir_si_intersection(x_min, y_min, taille_x, taille_y,
                                p_enn->coord_x, p_enn->coord_y, t_enn, t_enn))
        {
            return FAUX;
        }
        p_enn = p_enn->suiv;
    }
    /*
     * puisque cette fonction est aussi destinee aux ennemis,
     * il faut voir si le joueur bloque le passage !
     */
    if(voir_si_intersection(x_min, y_min, taille_x, taille_y,
                            bataille->coord_j_x + DECALAGE_X_J, bataille->coord_j_y + DECALAGE_Y_J,
                            TAILLE_JOUEUR, TAILLE_JOUEUR))
    {
        return FAUX;
    }
    /* tous les tests ont ete passes avec succes. Le mouvement va avoir lieu */
    return VRAI;
}



char rechercher_adversaires_dans_zone(struct etat_bataille* bataille,
                                      const struct projectile* p_proj,
                                      const unsigned int x_min_zone,
                                      const unsigned int y_min_zone,
                                      const unsigned char taille_x_zone,
                                      const unsigned char taille_y_zone,
                                      const struct base_donnees* donnees)
{
    char collision;
    struct ennemi* p_ennemi;
    struct ennemi* temp;
    unsigned char taille_ennemi;

    collision = FAUX;
    if(p_proj->allier)
    {
        /* recherche de collisions avec les ennemis */
        p_ennemi = bataille->ennemis.prem;
        while(p_ennemi)
        {
            taille_ennemi = get_taille(donnees, p_ennemi);
            if(voir_si_intersection(p_ennemi->coord_x, p_ennemi->coord_y,
                                    taille_ennemi, taille_ennemi,
                                    x_min_zone, y_min_zone,
                                    taille_x_zone, taille_y_zone))
            {
                infliger_degats(&p_ennemi->pv, p_proj);
                /*
                 * rappel : infliger_degat ne tue pas, il
                 * faut donc proceder a une verification
                 */
                if(!p_ennemi->pv)
                {
                    temp = p_ennemi;
                    p_ennemi = p_ennemi->suiv;
                    supprimer_ennemi(&bataille->ennemis, temp);
                }
                else
                    p_ennemi = p_ennemi->suiv;
                collision = VRAI;
            }
            else
                p_ennemi = p_ennemi->suiv;
        }
    }
    else
    {
        /* recherche de collision avec le joueur */
        if(voir_si_intersection(bataille->coord_j_x + DECALAGE_X_J,
                                bataille->coord_j_y + DECALAGE_Y_J,
                                TAILLE_JOUEUR, TAILLE_JOUEUR,
                                x_min_zone, y_min_zone,
                                taille_x_zone, taille_y_zone))
        {
            infliger_degats(&bataille->pv_j, p_proj);
            collision = VRAI;
            /*
             * voir_bilan_bataille se chargera de
             * voir si le joueur est mort
             */
        }
    }
    return collision;
}
